

#include "bspline.h"
#include "coord.h"
#include "bups.h"

#include <cmath>
#include <vector>
#include <cassert>

#include <iostream>

const static unsigned int BSPLINE_GRADO = 3;

Bspline::Bspline() : bases(BSPLINE_GRADO + 1) {}

void Bspline::validarIntegridad(const std::vector<Coord> &pcontrol) const {
   assert(Bspline::satisfacePrecond(pcontrol.size()));
}

size_t Bspline::grado() const {
   return BSPLINE_GRADO;
}

bool Bspline::satisfacePrecond(size_t cant) {
   if(not (cant > BSPLINE_GRADO)) { return false; }
   if(not (((cant - BSPLINE_GRADO - 1) % 2) == 0)) {return false;}

   return true;
}

Bspline::~Bspline() { }


/*
 * Retorna una coordenada de la curva, en funcion del parametro i
 *
 * Precondicion:
 *    0 <= i < 1
 * */
Coord Bspline::operator()(float i) const {
   float t;
   int enteroT;
   int puntos = pcontrol.size();

   this->parametros(i, t, enteroT);

   Coord resultado;

   float acum = 0;        
   for(int numBase = enteroT - BSPLINE_GRADO; numBase < enteroT + 1 ; ++numBase){

      float valorBase = bases.base(numBase, t);
      acum += valorBase;

      assert(numBase >= 0);
      assert(numBase < puntos);

      resultado += pcontrol[numBase] * valorBase;
   }

   assert(fabs(acum - 1.0) < 0.001);

   return resultado;
}

/*
 * Retorna un vector tangente a de la curva, en funcion del parametro i
 *
 * Precondicion:
 *    0 <= i < 1
 * */
Coord Bspline::tangente(float i) const {
   float t;
   int enteroT;

   this->parametros(i, t, enteroT);

   Coord resultado;

   for(int numBase = enteroT - BSPLINE_GRADO; numBase < enteroT + 1 ; ++numBase){
      float valorBase = bases.derivada(numBase, t);
      resultado += pcontrol[numBase] * valorBase;
   }

   return resultado;
}

void Bspline::parametros(float i, float &t, int &enteroT) const {
   assert(i >= 0);
   assert(i <= 1.0 + 0.001);
   size_t puntos = pcontrol.size();

   /*
    * i = 0 --> t = Grado
    * i = 1 --> t = puntos
    */

   assert(puntos >= BSPLINE_GRADO);
   t = (i*(puntos-BSPLINE_GRADO)) + BSPLINE_GRADO;

   /*
    * Debido a errores de redondeo hay que hacer esto. 
    * */

   enteroT = int(t);
   assert(enteroT <= int(puntos));
   if(enteroT == int(puntos)) {
      --enteroT;
   }

}

std::vector<Coord> Bspline::getPuntosControl(){
   return this->pcontrol;
}
